import { GlobalContainer } from '@/RoomDesign/common/GlobalContainer'
import { ThreeTDSLoader } from './loader/ThreeTDSLoader'
import { RoomZIPLoader } from '../resource-management/RoomZIPLoader'
import { DoubleSide, Group, Material, Mesh, MeshBasicMaterial } from 'three'

import type { DirBlobDataItem } from '../resource-management/entities'
import type { ThreeGLTFLoader } from './loader/ThreeGLTFLoader'
import { LoadTypeEnum } from '../common/InfrastructureEnum'

/**
 * 加载zip模型的代理类
 * 作用：存储通过压缩包加载而成的模型，为了避免对某个模型资源重复解压
 */
export class ThreeModelResourceManager {
  /**
   * 模型url与模型对象的map
   */
  private _urlToModelMap: Map<string, Group> = new Map()

  private roomZIPLoader: RoomZIPLoader
  private threeTDSLoader: ThreeTDSLoader
  private threeGLTFLoader: ThreeGLTFLoader
  public constructor() {
    this.roomZIPLoader = GlobalContainer.zipLoader
    this.threeTDSLoader = GlobalContainer.tdsLoader
    this.threeGLTFLoader = GlobalContainer.gltfLoader
  }

  public async loadModel(url: string) {
    // 检查是否有对应的资源缓存
    let modelGroup: Group | null
    modelGroup = this.getModelFromCache(url)

    if (modelGroup) {
      return modelGroup
    }

    const lowerUrl = url.toLowerCase()

    // 检查url是否为压缩包：zip \ rar
    const isZip = lowerUrl.endsWith('.zip')
    // const isRaR = url.endsWith('.rar')
    const isGLTF = lowerUrl.endsWith('.gltf')
    const isGLB = lowerUrl.endsWith('.glb')

    if (isZip) {
      modelGroup = await this.loadModelFromZip(url)
    } else {
      try {
        // TODO 非压缩包资源？
        if (isGLTF || isGLB) {
          modelGroup = await this.loadModelByType(LoadTypeEnum.GLTF, url)
        } else {
          console.error('不支持的压缩格式')
        }
      } catch (error) {
        console.error(error)
      }
    }

    modelGroup?.scale.set(100, 100, 100)

    return modelGroup
  }

  /** 加载模型（zip格式）, 3ds格式
   * @param url
   * @returns
   */
  public async loadModelFromZip(url: string): Promise<Group> {
    const modelGroup = new Group()

    // 读取zip文件中的所有文件，根据文件夹创建从对应的DirBlobDataItem
    const zipLoadResult: DirBlobDataItem[] = await this.roomZIPLoader.loadZipUrl(url)

    console.log(zipLoadResult)

    for (const dirBlobData of zipLoadResult) {
      const filePaths = Array.from(dirBlobData.blobPathUrlMap.keys())
      const check3DS = filePaths.findIndex((path) => path.endsWith('.3ds') || path.endsWith('.3DS'))
      const checkGLTF = filePaths.findIndex(
        (path) => path.endsWith('.gltf') || path.endsWith('.glb'),
      )

      const modelPath = filePaths.find((path) => path.endsWith('.3ds') || path.endsWith('.3DS'))

      if (!modelPath) {
        throw new Error('no found 3ds model file')
      }

      // 对同一个dirBolData，只解析一种格式的模型
      if (check3DS >= 0) {
      } else if (checkGLTF >= 0) {
      }

      const modelGroupItem = await this.threeTDSLoader.loadModel(modelPath, dirBlobData)
      // if (modelGroupItem) {
      // console.log('loadTDSZipModel success:', modelGroupItem)
      // GlobalContainer.threeSceneController.scene.add(modelGroupItem)
      // }

      modelGroup.add(modelGroupItem)
    }

    this.cacheModelResource(url, modelGroup)

    return modelGroup
  }

  public loadModelByType(loadType: LoadTypeEnum, url: string, dirBlobData?: DirBlobDataItem) {
    switch (loadType) {
      case LoadTypeEnum.GLTF:
        return this.threeGLTFLoader.loadModel(url, dirBlobData)
      case LoadTypeEnum.TDS:
        return this.threeTDSLoader.loadModel(url, dirBlobData)
      default:
        throw new Error('not support load type')
    }
  }

  //#region 缓存相关
  private cacheModelResource(url: string, model: Group) {
    this._urlToModelMap.set(url, model)
  }

  private getModelFromCache(url: string): Group | null {
    return this._urlToModelMap.get(url) || null
  }
  //#endregion 缓存相关

  //#region 材质替换相关
  private applyBasicMaterial(model: Group) {
    model.traverse((obj) => {
      console.log('obj:', obj)
      if (obj instanceof Mesh) {
        // 是数组
        if (obj.material instanceof Array) {
          for (let i = 0; i < obj.material.length; i++) {
            obj.material[i] = this.cloneToBasicMaterial(obj.material[i])
          }
        } else {
          obj.material = this.cloneToBasicMaterial(obj.material)
        }
      }
    })
  }

  private cloneToBasicMaterial(material: Material): MeshBasicMaterial {
    const materialAny = material as any

    const newMaterial = new MeshBasicMaterial({
      side: DoubleSide,
    })

    if (materialAny.color) {
      newMaterial.color = materialAny.color
    }

    if (materialAny.map) {
      newMaterial.map = materialAny.map
    }

    return newMaterial
  }

  //#endregion
}
